<?php

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 * Description of Crawler
 *
 * @author Bruno
 */
class Crawler {

    private $int_idCrawler;
    private $boo_pause = false;
    private $boo_crawlerAtivo = true;
    private $boo_bloqueado = false;
    private $boo_conteudo = false;
    private $str_site = '';
    private $str_charset = '';
    private $int_httpCodigo = -1;
    private $str_resumoErro = '';
    private $int_codigoErro = 0;
    private $str_codigoFonte = '';
    private $arr_tagsEPalavras;
    private $arr_serializado;

    function __construct($id_crawler) {
        $this->int_idCrawler = $id_crawler;
    }

    public function getStr_resumoErro() {
        return $this->str_resumoErro;
    }

    public function setStr_resumoErro($str_resumoErro) {
        $this->str_resumoErro = $str_resumoErro;
    }

    public function getInt_codigoErro() {
        return $this->int_codigoErro;
    }

    public function setInt_codigoErro($int_codigoErro) {
        $this->int_codigoErro = $int_codigoErro;
    }

    public function getInt_httpCodigo() {
        return $this->int_httpCodigo;
    }

    public function setInt_httpCodigo($int_httpCodigo) {
        $this->int_httpCodigo = $int_httpCodigo;
    }

    public function getBoo_conteudo() {
        return $this->boo_conteudo;
    }

    public function setBoo_conteudo($boo_conteudo) {
        $this->boo_conteudo = $boo_conteudo;
    }

    public function getArr_serializado() {
        return $this->arr_serializado;
    }

    public function setArr_serializado($arr_serializado) {
        $this->arr_serializado = $arr_serializado;
    }

    public function getInt_idCrawler() {
        return $this->int_idCrawler;
    }

    public function getBoo_pause() {
        return $this->boo_pause;
    }

    public function getBoo_crawlerAtivo() {
        return $this->boo_crawlerAtivo;
    }

    public function getBoo_bloqueado() {
        return $this->boo_bloqueado;
    }

    public function getStr_site() {
        return $this->str_site;
    }

    public function getStr_charset() {
        return $this->str_charset;
    }

    public function getStr_codigoFonte() {
        return $this->str_codigoFonte;
    }

    public function getArr_tagsEPalavras() {
        return $this->arr_tagsEPalavras;
    }

    public function setInt_idCrawler($int_idCrawler) {
        $this->int_idCrawler = $int_idCrawler;
    }

    public function setBoo_pause($boo_pause) {
        $this->boo_pause = $boo_pause;
    }

    public function setBoo_crawlerAtivo($boo_crawlerAtivo) {
        $this->boo_crawlerAtivo = $boo_crawlerAtivo;
    }

    public function setBoo_bloqueado($boo_bloqueada) {
        $this->boo_bloqueado = $boo_bloqueada;
    }

    public function setStr_site($str_site) {
        $this->str_site = $str_site;
    }

    public function setStr_charset($str_charset) {
        $this->str_charset = $str_charset;
    }

    public function setStr_codigoFonte($str_codigoFonte) {
        $this->str_codigoFonte = $str_codigoFonte;
    }

    public function setArr_tagsEPalavras($arr_tagsEPalavras) {
        $this->arr_tagsEPalavras = $arr_tagsEPalavras;
    }

    function IniciarCrawler() {
        $this->setStr_resumoErro('');
        $this->setInt_httpCodigo(-1);
        $this->setInt_codigoErro(0);


        if ($this->getBoo_crawlerAtivo()) {
            //Pause ativado
            while ($this->getBoo_pause()) {
                if ($this->getBoo_crawlerAtivo() == false) {
                    //Sai do laço pause
                    break;
                }
            }

            //Se crawler ativo
            if ($this->getBoo_crawlerAtivo()) {
                $this->setBoo_bloqueado(TRUE);

                //Armazena o codigo fonte na variavel $str_codigoFonte
                $this->setStr_codigoFonte($this->RequisitandoCodigoFonte($this->getStr_site(), $this->getStr_charset()));

                if ($this->getStr_codigoFonte() != false &&
                        $this->getStr_codigoFonte() != '') {

                    //Organiza em array todas as palavras e tags
                    $this->setArr_tagsEPalavras($this->RefinarTagsEPalavras($this->getStr_codigoFonte()));
                    if (count($this->getArr_tagsEPalavras()) > 0) {

                        //Retira as tags sem conteudo : <p> </p>
                        $this->setArr_tagsEPalavras($this->FormatarElementosVazios($this->getArr_tagsEPalavras()));
                        if (count($this->getArr_tagsEPalavras()) > 0) {

                            //Serializar o conteudo dividindo em matriz  
                            $this->setArr_serializado($this->SerializarConteudo($this->getArr_tagsEPalavras()));

                            //Finalizado com sucesso
                            $this->setStr_resumoErro('Finalizado com sucesso.');
                            $this->setInt_codigoErro(1);
                            $this->TesteExibir();
                        } else {
                            //Erro: Sem elementos pós Formatar
                            $this->setStr_resumoErro('Sem elementos pós "Formatar elementos vazios".');
                            $this->setInt_codigoErro(-4);
                        }
                    } else {
                        //Erro: Sem elementos pós refinamento
                        $this->setStr_resumoErro('Sem elementos pós "Refinar tags e palavras".');
                        $this->setInt_codigoErro(-3);
                    }
                } else {
                    //Erro: Requisitar codigo
                    //Pagina não ONLINE
                    if ($this->getInt_httpCodigo() !== 200) {
                        $this->setStr_resumoErro('Pagina não encontrada.');
                        $this->setInt_codigoErro(-20);

                        //Pagina não BAIXADA
                    } else if ($this->getInt_httpCodigo() === 200 && $this->getStr_codigoFonte() == false) {
                        $this->setStr_resumoErro('Pagina indisponivel no momento.');
                        $this->setInt_codigoErro(-21);
                    }
                }
            } else {
                $this->setStr_resumoErro('Crawler desativado');
                $this->setInt_codigoErro(-1);
            }
        } else {
            $this->setStr_resumoErro('Crawler desativado');
            $this->setInt_codigoErro(-1);
        }
//        echo 'Passou teste exibir<br>';


        $this->LimparEDesbloquerCrawler();
    }

//fim funcao

    function RequisitarPaginas() {
//Envia requisicao com os resul
    }

    /*
     * @param  $str_par_site : Url da pagina
     * @param  $str_charset : charset da pagina requisitada
     * @return $str_ret_codigoFonteSite : codigo fonte
     */

    function RequisitandoCodigoFonte($str_par_site, $str_charset = 'UTF-8') {
        /*
         * Metodos da classe cURL
         * http://translate.google.com.br/translate?hl=pt-BR&sl=en&u=http://www.php.net/manual/en/function.curl-setopt.php&prev=/search%3Fq%3Dphp%2Bcurl%2Bcharset%26es_sm%3D93
         */
        set_time_limit(0);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $str_par_site);
        //curl_setopt($ch, CURLOPT_HEADER, true);//Cabeçalho/header de informações
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //Nega verificação do HTTPS
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_REFERER, '');
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_ENCODING, $str_charset);
        $str_ret_codigoFonteSite = curl_exec($ch);

        //Guardando o HttpCodigo
        $int_httpCodigo = (curl_getinfo($ch, CURLINFO_HTTP_CODE));
        $this->setInt_httpCodigo($int_httpCodigo);

        curl_close($ch);

        //echo  $str_ret_codigoFonteSite;

        return $str_ret_codigoFonteSite;
    }

    function RefinarTagsEPalavras($str_par_Codigo) {
//Troca por '' (Sem espaço)
        $arr_padroes1 = array(
            '/((<script(.*?)*>)(.*?)(<\/script>))/sim', //tag scripts
            '/((<!--)(.*?)(-->))/sim', //Comentarios: <!-- ... -->
            '/((<![CDATA[)(.*?)(]]>))/sim', //Comentarios: <![CDATA[...]]>
            '/((\/\*)(.*?)(\*\/))/sim', //Comentarios /* ... * /
            '/((<!)(.*?)(>))/sim', //Doctype <!DOCTYPE html>
            '/((<form(.*?)?>)(.*?)(<\/form>))/sim', //tag: <form>...</form>
            '/((<button(.*?)?>)(.*?)(<\/button>))/sim', //tag: <form>...</form>
            '/((<noscript(.*?)?>)(.*?)(<\/noscript>))/sim', //tag: <noscript>...</noscript>
            '/<br\s*(.*?\s*)*(\/)?>/sim', //tag: <br>|<br >|<br />
            '/<hr\s*(.*?)*>/sim', //tag: <hr>|<hr ...>
            '/((<img(\s*?).*?(\/>|>)))/sim', //tag: <img .../> ou <img ...>
            '/((<meta(\s*?).*?(\/>|>)))/sim', //tag: <img .../> ou <img ...>
            '/((<link(\s*?).*?(\/>|>)))/sim', //tag: <img .../> ou <img ...>
            '/((<input(\s*?).*?(\/>|>)))/sim', //tag: <img .../> ou <img ...>
            '/((<style(.*?)?>)(.*?)(\/style>))/sim', //tag <style>...</style>
            '/((<iframe(.*?)?>)(.*?)(\/iframe>))/sim', //tag <iframe>...</iframe>
            '/[\t\n\r\f\v]/sim', //Formatações: \n\r\t
            '/(&nbsp;)/sim', //char esecial espaço
            '/\s{2,}/sim');  //Espaços duplos ou mais
//Troca por '>'
        $arr_padroes2 = array('/(\s*)>/sim', //retira espaços extras
            '/((\s{1,})([a-zA-Z0-9]*)=.*?(>))/sim', //reparar _href=...> 
            '/(\s*[a-z0-9-:_]*(=)(\".*?\"|\'.*?\'|\s{0}.*?\s{1}))*>/sim', //reparar _href=...>
        );

        $arr_padroes3 = array('/></sim'); //reparar >< para > <
        $arr_padroes4 = array('/>s{0}/sim'); //reparar >alou  > alou
        $arr_padroes5 = array('/s{0}</sim'); //reparar  alou<    alou <
        $arr_padroes6 = array('/\s{2,}/sim'); //retira espaços extras
        $arr_padroes7 = array('/(<\/html(.*)*)/sim'); //retira tudo pós html
        $arr_padroes8 = array('/(.*?<html>)/sim'); //retira tudo antes html

        /*
         * htmlspecialchars : &lt;b&gt;bold&lt;/b&gt;  =>  <b>bold</b>
         * html_entity_decode : &lt;&copy; W3S&ccedil;h&deg;&deg;&brvbar;&sect;&gt;  =>  <© W3Sçh°°¦§>
         */

//        $str_par_Codigo = htmlspecialchars($str_par_Codigo);
//        $str_par_Codigo = html_entity_decode($str_par_Codigo);
        $str_par_Codigo = (trim(preg_replace($arr_padroes1, ' ', $str_par_Codigo)));
        $str_par_Codigo = (trim(preg_replace($arr_padroes2, '>', $str_par_Codigo)));
        $str_par_Codigo = (trim(preg_replace($arr_padroes3, '> <', $str_par_Codigo)));
        $str_par_Codigo = (trim(preg_replace($arr_padroes4, '> ', $str_par_Codigo)));
        $str_par_Codigo = (trim(preg_replace($arr_padroes5, ' <', $str_par_Codigo)));
        $str_par_Codigo = (trim(preg_replace($arr_padroes6, ' ', $str_par_Codigo)));
        $str_par_Codigo = (trim(preg_replace($arr_padroes7, '</html>', $str_par_Codigo)));
        $str_par_Codigo = (trim(preg_replace($arr_padroes8, '<html>', $str_par_Codigo)));

//Quebra nos espaçõs para formar o array
        $arr_par_palavras = explode(' ', $str_par_Codigo);

        return $arr_par_palavras;
    }

    /*
     * @param  $arr_par_TagsEPalavras : Resultado do metodo RequisitandoCodigoFonte
     * @return $arr_par_TagsEPalavras : Array sem tag vazias
     */

    function FormatarElementosVazios($arr_par_TagsEPalavras = null) {
        $int_qtdPalavras = 0;
        $boo_reindexaArray;
        $int_lacoDo = 0;
        $int_lacoFor = 0;
        $str_palavraInicial = '';
        $str_palavraFinal = '';
        $str_padraoAberturaTag = '/^(<){1}[^\/]{1}[a-z]+(>){1}$/'; //<aaaa>
        $str_padraoFechamentoTag = '/^(<){1}(\/){1}[a-z]+(>){1}$/'; //</aaaa>

        do {
            $boo_reindexaArray = false;
            $int_qtdPalavras = count($arr_par_TagsEPalavras) - 1;

            for ($index = 0; $index < $int_qtdPalavras; $index++, $int_lacoFor++) {

                @$str_palavraInicial = ($arr_par_TagsEPalavras[$index]);
                $str_palavraFinal = ($arr_par_TagsEPalavras[$index + 1]);


                if ((preg_match($str_padraoAberturaTag, $str_palavraInicial)) &
                        (preg_match($str_padraoFechamentoTag, $str_palavraFinal))) {

//Retirando o conteudo da tag
                    $str_palavraInicial = substr($str_palavraInicial, 1, -1);
                    $str_palavraFinal = substr($str_palavraFinal, 2, -1);

                    if ($str_palavraInicial === $str_palavraFinal) {
//  echo '<br>Conteudo:' . $str_palavraInicial . ' -- (' . $index.','.($index+1).').';
                        unset($arr_par_TagsEPalavras[$index]);
                        unset($arr_par_TagsEPalavras[($index + 1)]);
                        $boo_reindexaArray = true;
                    }//fim if conteudo
                }//fim if pregmatch
            }//Final for

            /* Reindexa o array retirando os espaços nullo
             * Gerado pelo unset($arr_par_TagsEPalavras[POSICAO]);
             */
            $arr_par_TagsEPalavras = array_values($arr_par_TagsEPalavras);
            flush();
//Retorna o ponteiro para a posição 0 do array
            reset($arr_par_TagsEPalavras);

//Incrementa o contador do laco DO
            $int_lacoDo++;
        } while ($boo_reindexaArray == true);


        return $arr_par_TagsEPalavras;
    }

    /*
     * @param  $arr_par_TagsEPalavras : Resultado do metodo FormatarElementosVazios
     * @return $arr_serializacaoCompleta : Matriz com palavras e caminhos
     * Ex.:
     *      Array(
     *              array[0]('palavra0','caminho0'
     *                      )
     *              array[1]('palavra1','caminho1'
     *                      )
     *              array[N]('palavraN','CaminhoN'
     *                      )
     *      )
     */

    function SerializarConteudo($arr_par_TagsEPalavras = null) {

        $str_palavraTag = '';
        $str_caminhoSerializacaoAtual;
        $boo_achouTagSolo = false;
        $str_linhaSerializacao = '';
        $arr_serializacaoCompleta = array();
        $str_padraoAberturaTag = '/^(<){1}[a-z0-9]+(>){1}$/'; //   <aaaa>
        $str_padraoFechamentoTag = '/^(<){1}(\/){1}[a-z0-9]+(>){1}$/'; //   </aaaa>
        $arr_caminhoSerializacao = array();
        $int_qtdPalavras = count($arr_par_TagsEPalavras);

        for ($index = 0; $index < $int_qtdPalavras; $index++) {

//Retira o conteudo do indice do array
            $str_palavraTag = ($arr_par_TagsEPalavras[$index]);

//Verifica se é tag de ABERTURA
            if (preg_match($str_padraoAberturaTag, $str_palavraTag)) {
//Guarda o conteudo para o caminho (tagHtml)
                $str_palavraTag = substr($str_palavraTag, 1, -1);
                $arr_caminhoSerializacao[] = ( $str_palavraTag);

//Verifica se é tag de FECHAMENTO
            } elseif (preg_match($str_padraoFechamentoTag, $str_palavraTag)) {

//Retira o conteudo do indice do array : (</TAG>)  =>  (TAG)
                $str_palavraTag = substr($str_palavraTag, 2, -1);

//Retira o conteudo do indice do array 
                $str_ultimaPosicArray = $arr_caminhoSerializacao[count($arr_caminhoSerializacao) - 1];
                $str_ultimaPosicArray = substr($str_ultimaPosicArray, 0, strlen($str_ultimaPosicArray));

                flush();

//Verifica se a tag de fechamento e a ultima que foi aberta.
                if ($str_palavraTag == $str_ultimaPosicArray) {

//Retirando a ultima posicao do caminho
                    $arr_caminhoSerializacao = array_slice($arr_caminhoSerializacao, 0, count($arr_caminhoSerializacao) - 1);
                } else {
//                    echo '<hr>Tratamento para < / ' . $str_palavraTag . ' ><br>';
//TRatamento para normalizacao de tag
                    $boo_achouTagSolo = false;
                    $int_cont = count($arr_caminhoSerializacao) - 1;
                    for (; $int_cont > 0; $int_cont--) {
//Retira a palavra do array junto com o '>'
                        $str_tag = $arr_caminhoSerializacao[$int_cont];

                        if ($str_tag == $str_palavraTag) {
//                            echo 'Normalizar array posicao: ' . $int_cont . ' a ' . (count($arr_caminhoSerializacao) - 1) . '<br>';
                            $boo_achouTagSolo = true;

                            $arr_caminhoSerializacao = array_slice($arr_caminhoSerializacao, 0, $int_cont);

                            break;
                        }
                    }//Final for
                    if (!$boo_achouTagSolo) {
//                        echo 'Tag problema. (IGNORADA)<hr>';
                    }
                }
//É conteudo e nao tags
            } else {

                //Se não for espaço
                if ($str_palavraTag != "&#160;" &&
                        $str_palavraTag != "Â&nbsp;" &&
                        $str_palavraTag != "&nbsp;"
                ) {

                    //Adiciona caminho : Ajunta o array colocando '>' entre os elementos
                    //guardando em formato string
                    $str_caminhoSerializacaoAtual = implode('>', $arr_caminhoSerializacao);

//Adiciona um array ao array final formando uma matriz
                    $arr_serializacaoCompleta[] = (($str_palavraTag . ' :# ' . $str_caminhoSerializacaoAtual));
                }
            }
        }//Final for

        $int_qtdPalavras = count($arr_serializacaoCompleta);


        //Numerando a posicao da palavra no site
        for ($index = 0; $index < $int_qtdPalavras; $index++) {
            //echo 'palavra ' . (($index) + 1) . '    ' .  $this->getArr_serializado()[$index] . '<br>';
            $arr_serializacaoCompleta[$index] = (($index) + 1) . ' :# ' . $arr_serializacaoCompleta[$index];
        }

//Retorn a matriz
        return $arr_serializacaoCompleta;
    }

    function LimparEDesbloquerCrawler() {
        //Informa que ja tem conteudo pronto
        $this->setBoo_conteudo(TRUE);

        //Limpando os atributos
        $this->setStr_codigoFonte('');
        $this->setArr_tagsEPalavras(null);

        //Desbloqueia o crawler para manutencção
        $this->setBoo_bloqueado(FALSE);
    }

    function TesteExibir() {
        // Abre ou cria o arquivo bloco1.txt
        // "a" representa que o arquivo é aberto para ser escrito
        $fp = fopen("C:\blocoCrawler.txt", "w");
        $data = 'Data:' . date("d/m/Y H:i:s ");
        // Escreve "exemplo de escrita" no bloco1.txt
        $escreve = fwrite($fp, 'O site organizado: "' . $this->getStr_site() . '"' . $data . PHP_EOL);

        $int_qtdPalavras = count($this->getArr_serializado());
//        echo 'Exibindo resultado<br>';
//        echo '<pre>';
//        if (ob_get_level() == 0)
//            ob_start();

        for ($index = 0; $index < $int_qtdPalavras; $index++) {

            // Escreve "exemplo de escrita" no bloco1.txt
            $escreve = fwrite($fp, $this->getArr_serializado()[$index] . PHP_EOL);
            
            echo $this->getArr_serializado()[$index] . '<br>';
//            ob_flush();
//            flush();
        }
//        echo '</pre><hr>';
//        ob_end_flush();
// Fecha o arquivo
        fclose($fp);
    }

}

//Testando o crawleer

$c = new Crawler(1);
//$c->setStr_site('https://labs.moip.com.br/forum/archive/index.php/t-2042.html');
//$c->setStr_site('https://www.google.com/');
$c->setStr_site('http://creativecommons.org/licenses/by-nc-sa/3.0/');
//$c->setStr_site('http://www.terra.com.br');
//$c->setStr_site('www.uol.com.br');
$c->IniciarCrawler();
